﻿
using EmperialApps.WeatherSpark.Data;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Threading;

namespace EmperialApps.WeatherSpark.Service.Internal {

    /// <summary>Generates and stores forecast images using an Azure blob container.</summary>
    internal sealed class ForecastImageGenerator {

        /// <summary>Initializes a new instance of the <see cref="ForecastImageGenerator"/> class.</summary>
        public ForecastImageGenerator( CloudStorageAccount account, ForecastSource source ) {
            this._source = source;

            // Retrieve image storage container.
            var client = account.CreateCloudBlobClient( );
            this._imageContainer = client.GetContainerReference( source.ContainerName + ImageSuffix );
            this._imageContainer.CreateIfNotExists( );
            this._imageContainer.SetPermissions( new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Container } );

            // Setup image creator thread.
            this._imageProcessorThread = new Thread( this.CreateForecastImages ) {
                IsBackground = true,
                Name = "WS" + ImageSuffix
            };
            this._imageProcessorThread.SetApartmentState( ApartmentState.STA );
            this._imageProcessorThread.Start( );
        }


        /// <summary>Adds the specified forecast to the image creation list.</summary>
        public bool Queue( Forecast forecast ) {
            return !this._forecasts.IsAddingCompleted
                && this._forecasts.TryAdd( forecast );
        }

        /// <summary>Ends image creation.</summary>
        public void Stop( ) {
            if( !this._forecasts.IsAddingCompleted ) {
                this._forecasts.CompleteAdding( );
                this._imageProcessorThread.Join( TimeSpan.FromSeconds( 0.5 ) );
            }
        }


        #region Private Members

        private const string ImageSuffix = "-images";

        private readonly BlockingCollection<Forecast> _forecasts = new BlockingCollection<Forecast>( );
        private readonly CloudBlobContainer _imageContainer;
        private readonly Thread _imageProcessorThread;
        private readonly ForecastSource _source;


        private void CreateForecastImages( ) {
            try {
                using( var imageStream = new MonitoredMemoryStream( this._source + " tile image" ) )
                    foreach( Forecast forecast in this._forecasts.GetConsumingEnumerable( ) ) {
                        TraceEventType.Information.Trace( "Creating {0} tile images for forecast {1}", this._source, forecast );

                        foreach( Units units in new[] { Units.Metric, Units.Imperial } )
                            foreach( var mode in new[] { ForecastDisplayMode.Day, ForecastDisplayMode.Hour } ) {
                                string tileName = forecast.Location.GetTileName( units, mode );
                                try {
                                    imageStream.SetLength( 0 );
                                    TileImage.SaveImage( imageStream, forecast, units, mode );

                                    CloudBlockBlob blob = this._imageContainer.GetBlockBlobReference( tileName + ".png" );
                                    imageStream.SaveToBlob( blob );
                                }
                                catch( TypeInitializationException ) {
                                    throw;
                                }
                                catch( Exception ex ) {
                                    string message = this._source + " " + tileName + " tile Image Failure";
                                    if( Extensions.IsTimeoutException( ex as StorageException ) )
                                        ex.Trace( message );
                                    else
                                        ex.Report( message );
                                }
                            }
                    }

                TraceEventType.Information.Trace( this._source + " tile image creation finished." );
            }
            catch( Exception ex ) {
                ex.Report( this._source + " Tile Image Failure" );
            }
        }

        #endregion
    }

}
